#ifndef __TINY_TEST__

#include "vbb_GSM_Communicator.h"
#include "vbb_GSM.h"
#include "vbb_FirmwareUtils.h"
#include "fw/vbb_RTC.h"
#include <stdio.h>

#include "vbb_GSM_CNS01.h"
#include "vbb_GSM_CNS03.h"
#include "vbb_GSM_BA01.h"

char communication_state;
char communication_tcp_status;

char * communication_send_buffer;
char * communication_recv_buffer;
int communication_send_size;
int communication_recv_size;

unsigned int communication_start_time;
unsigned int communication_allow_time;

char communication_server_addr[256];
char communication_http_doc[256];
char _serverIP[64];
char _serverPort[16];

bool communication_inprocess;
bool communication_needzip;
bool communication_needKeepAlive;
bool communication_success;
unsigned int server_online;

bool GSM_NetworkReg(void)
{
    int c = GSM_Command("AT+CREG?\r", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", 2);
    return (c == 1) || (c == 4);
}

char GSM_NetworkSet(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_NetworkSet_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_NetworkSet_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_NetworkSet_BA01();
    else
        return 0;
}

char GSM_GPRS_Open(void)
{  
    if (vbb_DeviceModel() == CNS01)
        return GSM_GPRS_Open_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_GPRS_Open_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_GPRS_Open_BA01();
    else
        return 0;
}

char GSM_GPRS_WaitForOpen(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_GPRS_WaitForOpen_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_GPRS_WaitForOpen_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_GPRS_WaitForOpen_BA01();
    else
        return 0;
}

char GSM_TCP_Open(const char * ServerAddr)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_Open_CNS01(ServerAddr);
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_Open_CNS03(ServerAddr);
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_Open_BA01(ServerAddr);
    else
        return 0;
}

char GSM_TCP_WaitForOpen(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_WaitForOpen_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_WaitForOpen_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_WaitForOpen_BA01();
    else
        return 0;
}

char GSM_TCP_Opened(const char * Document, char * DataBuffer, int * DataSize, char * TmpBuffer)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_Opened_CNS01(Document, DataBuffer, DataSize, TmpBuffer);
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_Opened_CNS03(Document, DataBuffer, DataSize, TmpBuffer);
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_Opened_BA01(Document, DataBuffer, DataSize, TmpBuffer);
    else
        return 0;
}

char GSM_TCP_DataWaitForReady(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_DataWaitForReady_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_DataWaitForReady_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_DataWaitForReady_BA01();
    else
        return 0;
}

int GSM_TCP_DataPrepareSend(char * DataBuffer, int DataSize)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_DataPrepareSend_CNS01(DataBuffer, DataSize);
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_DataPrepareSend_CNS03(DataBuffer, DataSize);
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_DataPrepareSend_BA01(DataBuffer, DataSize);
    else
        return 0;
}

int GSM_TCP_DataSendData(char * DataBuffer, int SendSize)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_DataSendData_CNS01(DataBuffer, SendSize);
    else if (vbb_DeviceModel() == CNS03)        
        return GSM_TCP_DataSendData_CNS03(DataBuffer, SendSize);
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_DataSendData_BA01(DataBuffer, SendSize);
    else
        return 0;
}

char GSM_TCP_WaitForServerClosed(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_WaitForServerClosed_CNS01();
    else if (vbb_DeviceModel() == CNS03)        
        return GSM_TCP_WaitForServerClosed_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_WaitForOpen_BA01();
    else
        return 0;
}

char GSM_TCP_CloseClient(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_CloseClient_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_CloseClient_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_CloseClient_BA01();
    else
        return 0;
}

int GSM_TCP_ReceiveData(char * ResponseBuffer)
{
    char * _tmp2;
    int contentLength;
    int i;
    int recvLength = -1;
    
    if (vbb_FindStr(ResponseBuffer, "HTTP/1.1 200 OK\r\n"))
    {                            
        //vbb_OUTPUT_Print("INFO: HTTP communication finished successfully.\r\n");
        _tmp2 = vbb_FindStr(ResponseBuffer, "\r\nContent-Length:");
        if (!_tmp2)
            _tmp2 = vbb_FindStr(ResponseBuffer, "\r\nContent-length:");
        if (_tmp2)
        {
            _tmp2 += vbb_GetLen("\r\nContent-Length:");
            sscanf(_tmp2, "%d", &contentLength);
            _tmp2 = vbb_FindStr(_tmp2, "\r\n\r\n");
            //vbb_CONSOLE_Printf("DEBUG: Size of received content: %d\r\n", contentLength);
            _tmp2 += vbb_GetLen("\r\n\r\n");
            for (i = 0; i < contentLength; i++)
            {
                ResponseBuffer[i] = _tmp2[i];
                if (vbb_DebugMode())
                    vbb_OUTPUT_WriteByte(_tmp2[i]);
            }
            ResponseBuffer[contentLength] = 0;
            recvLength = contentLength;                
        }
    }
        
    return recvLength;
}

void GSM_BuildDataBuffer(const char * Document, char * DataBuffer, int * DataSize, char * TmpBuffer)
{
    sprintf(TmpBuffer, "POST /%s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", Document, _serverIP, *DataSize);    
    vbb_StrCpy(TmpBuffer + vbb_GetLen(TmpBuffer), DataBuffer, *DataSize);
    *DataSize += vbb_GetLen(TmpBuffer);
    vbb_StrCpy(DataBuffer, TmpBuffer, *DataSize);
}

bool GSM_Communication_Start(const char * URL, char * DataBuffer, int DataSize, char * RecvBuffer, bool NeedZip, bool NeedKeepAlive, unsigned int TimeOutSec)
{
    const char * _tmp;
    char i;
    char j;
    
    if (GSM_Communication_InProcess())
        return FALSE;
    
    communication_inprocess = TRUE;
    communication_send_buffer = DataBuffer;
    communication_send_size = DataSize;
    communication_recv_buffer = RecvBuffer;        
    communication_recv_size = 0;
    communication_recv_buffer[0] = 0;
    communication_needzip = NeedZip;
    communication_needKeepAlive = NeedKeepAlive;
    communication_state = COMMUNICATION_STATE_INIT;
    communication_start_time = vbb_RTC_GetSec();
    communication_allow_time = TimeOutSec;
    
    vbb_ExtractStr(communication_server_addr, URL, "http://", "/");
    _tmp = URL + vbb_GetLen(communication_server_addr) + vbb_GetLen("http://") + 1;
    vbb_StrCpy(communication_http_doc, _tmp, vbb_GetLen(_tmp));
    
    vbb_OUTPUT_Print("INFO: HTTP Communication start.\r\n");
    vbb_OUTPUT_Print("INFO: Server Addr \"");
    vbb_OUTPUT_PrintDebug(communication_server_addr);
    vbb_OUTPUT_Print("\"\r\n");
    vbb_OUTPUT_Print("INFO: HTTP Document \"");
    vbb_OUTPUT_PrintDebug(communication_http_doc);
    vbb_OUTPUT_Print("\"\r\n");
    
    for (i = 0; i < vbb_GetLen(communication_server_addr); i++)
        if (communication_server_addr[i] != ':')
            _serverIP[i] = communication_server_addr[i];
        else
            break;    
    i++;
    j = i;
    for (; i < vbb_GetLen(communication_server_addr); i++)
        _serverPort[i - j] = communication_server_addr[i];
    
    communication_success = FALSE;
    
    return TRUE;
}

int GSM_Communication_ReceivingData(char * Buffer)
{
    if (vbb_DeviceModel() == CNS01)        
        return GSM_Communication_ReceivingData_CNS01(Buffer);
    else if (vbb_DeviceModel() == CNS03)        
        return GSM_Communication_ReceivingData_CNS03(Buffer);
    else if (vbb_DeviceModel() == BA01)
        return GSM_Communication_ReceivingData_BA01(Buffer);
    return 0;
}

bool GSM_IsInCommunication(void)
{
    return communication_inprocess;
}

extern int lzjb_compress(char *s_start, char *d_start, int s_len);

bool GSM_Communication_InProcess(void)
{
    return communication_inprocess;
}

int communication_send_buffer_size;

bool HTTP_Success;

char tmp_recv_buffer[1024];

bool GSM_ParseHTTP(char * Buffer, int BufferSize)
{
    char * _lengthPos;
    char * _endLine;
    int _contentLength;
    int _remainSize;    
    
    /*
    vbb_OUTPUT_PrintDebug(Buffer);
    vbb_OUTPUT_Print("\r\n");
    */
    
    _lengthPos = vbb_FindStr(Buffer, "Content-Length: ");
    if (_lengthPos == NULL)
        _lengthPos = vbb_FindStr(Buffer, "Content-length: ");
    
    if (_lengthPos)
    {                
        _endLine = vbb_FindStr(_lengthPos, "\r\n\r\n");   
        if (_endLine)
        {            
            _endLine += vbb_GetLen("\r\n\r\n");
            _remainSize = BufferSize - (_endLine - Buffer);
            sscanf(_lengthPos + vbb_GetLen("Content-length: "), "%d", &_contentLength);
            
            if (_remainSize >= _contentLength)
            {
                vbb_OUTPUT_Print("INFO: Data from server is fully received.\r\n");
                return TRUE;
            }
            else
                return FALSE;
        }
        else
            return FALSE;
    }
    else
        return FALSE;
}

bool GSM_ParseCheapProtocol(char * Buffer, int BufferSize)
{
    return FALSE;
}

bool GSM_GPRS_IsOpen(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_GPRS_IsOpen_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_GPRS_IsOpen_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_GPRS_IsOpen_BA01();
    return FALSE;
}

bool GSM_TCP_IsOpen(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_TCP_IsOpen_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_TCP_IsOpen_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_TCP_IsOpen_BA01();
    return FALSE;
}

unsigned int communication_wait_start;
unsigned int communication_lastsuccessful_time;
int communication_rate_int;
int communication_rate_fra;
extern unsigned int GSM_State;
int timeOutCount = 0;


bool GSM_Communication_Update(void)
{
    int ret;
    int _zippedSize;
    unsigned int temp_int;
    
    if (!GSM_Communication_InProcess())
        return FALSE;
    
    if (vbb_RTC_GetSec() - communication_start_time > communication_allow_time)
    {
        communication_state = COMMUNICATION_STATE_TCP_CLOSE;
        vbb_OUTPUT_Print("INFO: Communication cancelled because of time out.\r\n");
        
        timeOutCount++;
        if (timeOutCount == 3)
        {
            timeOutCount = 0;
            GSM_State = GSM_BEFORE_REBOOT;
            vbb_OUTPUT_Print("INFO: Reboot GSM module due to too many time out.\r\n");
        }
    }
    
    switch (communication_state)
    {
        case COMMUNICATION_STATE_INIT:
        {            
            HTTP_Success = FALSE;
            communication_state = COMMUNICATION_STATE_NETWORK_REG;            
            break;
        }
        
        case COMMUNICATION_STATE_NETWORK_REG:
        {
            if (GSM_GPRS_IsOpen())
            {
                communication_state = COMMUNICATION_STATE_GPRS_WAITFOROPEN;
            }
            else if (GSM_NetworkReg())
            {
                GSM_NetworkSet();
                communication_state = COMMUNICATION_STATE_GPRS_OPEN;
            }
            else
            {
                communication_allow_time = 120;
            }
            break;
        }
        
        case COMMUNICATION_STATE_GPRS_OPEN:
        {
            if (GSM_GPRS_Open())
                communication_state = COMMUNICATION_STATE_GPRS_WAITFOROPEN;
            else
            {
                if (GSM_GPRS_IsOpen())
                {
                    communication_state = COMMUNICATION_STATE_TCP_OPEN;
                    vbb_OUTPUT_Print("INFO: GPRS attached.\r\n");
                }
                else
                {
                    communication_state = COMMUNICATION_STATE_TCP_CLOSE;
                    vbb_OUTPUT_Print("INFO: Communication cancelled because of GSM error.\r\n");
                }
            }
            break;
        }
        
        case COMMUNICATION_STATE_GPRS_WAITFOROPEN:
        {         
            ret = GSM_GPRS_WaitForOpen();
            if (ret == GPRS_ATTACHED)
            {
                communication_state = COMMUNICATION_STATE_TCP_OPEN;
                vbb_OUTPUT_Print("INFO: GPRS attached.\r\n");
            }
            else if (ret == GPRS_FAILED)
            {
                communication_success = FALSE;     
                communication_state =  COMMUNICATION_STATE_TCP_CLOSE;
                vbb_OUTPUT_Print("INFO: Communication cancelled because of GPRS out of service.\r\n");
            }            
            break;
        }
        
        case COMMUNICATION_STATE_TCP_OPEN:
        {
            if (GSM_TCP_IsOpen())
            {
                communication_state =  COMMUNICATION_STATE_DATA_PREPARE_SEND;
                vbb_OUTPUT_Print("INFO: TCP connection already established.\r\n");
                server_online = vbb_RTC_GetSec();
            }
            else
            {
                ret = GSM_TCP_Open(communication_server_addr);
                communication_state = COMMUNICATION_STATE_TCP_WAITFOROPEN;
            }
            break;
        }
        
        case COMMUNICATION_STATE_TCP_WAITFOROPEN:
        {
            ret = GSM_TCP_WaitForOpen();
            if (ret == TCP_CONNECTION_UP)
            {
                communication_state =  COMMUNICATION_STATE_DATA_PREPARE_SEND;
                vbb_OUTPUT_Print("INFO: TCP connection established.\r\n");
            }
            else if (ret == TCP_CONNECTION_DOWN)
            {
                communication_success = FALSE;     
                communication_state =  COMMUNICATION_STATE_TCP_CLOSE;                
                vbb_OUTPUT_Print("INFO: TCP connection failed.\r\n");
            }
            break;
        }                
        
        case COMMUNICATION_STATE_DATA_PREPARE_SEND: //zip data before sending
        {            
            if (communication_needzip && communication_send_size > 0)
            {
                _zippedSize = lzjb_compress(communication_send_buffer, communication_recv_buffer, communication_send_size);
                if (_zippedSize < communication_send_size)
                {
                    vbb_StrCpy(communication_send_buffer, communication_recv_buffer, _zippedSize);                    
                    vbb_CONSOLE_Printf("INFO: Data compressed successfully in %d bytes (%d%%).\r\n", _zippedSize, _zippedSize * 100 / communication_send_size);
                    communication_send_size = _zippedSize;
                }
                else
                {               
                    communication_success = FALSE;                    
                    communication_state = COMMUNICATION_STATE_TCP_CLOSE;                    
                    vbb_OUTPUT_Print("INFO: Communication cancelled because of its data size is not worth sending.\r\n");
                }
            }
            
            GSM_BuildDataBuffer(communication_http_doc, communication_send_buffer, &communication_send_size, communication_recv_buffer);             
            vbb_CONSOLE_Printf("INFO: Prepared %d bytes to send.\r\n", communication_send_size);
            
            communication_send_buffer_size = GSM_TCP_DataPrepareSend(communication_send_buffer, communication_send_size);
            communication_state = COMMUNICATION_STATE_DATA_SENDING;
            break;
        }
        
        case COMMUNICATION_STATE_DATA_SENDING:
        {
            ret = GSM_TCP_DataWaitForReady();            
            if (ret)   
            {                
                ret = GSM_TCP_DataSendData(communication_send_buffer, communication_send_buffer_size);                
                if (ret > 0)
                {
                    communication_send_buffer += ret;
                    communication_send_size -= ret;
                    vbb_CONSOLE_Printf("INFO: Transfered %d bytes to sending buffer.\r\n", ret);
                    if (communication_send_size > 0) 
                    {
                        communication_send_buffer_size = GSM_TCP_DataPrepareSend(communication_send_buffer, communication_send_size);                                        
                    }
                    else
                    {
                        communication_state = COMMUNICATION_STATE_WAIT_FOR_SERVER;
                        communication_wait_start = vbb_RTC_GetSec();                        
                        vbb_OUTPUT_Print("INFO: All data sent to sending buffer.\r\n");
                    }
                }                
                else if (ret < 0)
                {
                    communication_success = FALSE;                    
                    communication_state = COMMUNICATION_STATE_TCP_CLOSE;                    
                    vbb_OUTPUT_Print("ERROR: Communication cancelled because GSM buffer overloaded.\r\n");
                }
            }
            break;
        } 

        case COMMUNICATION_STATE_WAIT_FOR_SERVER:
        {
            if (vbb_RTC_GetSec() - communication_wait_start > 1)
                communication_state = COMMUNICATION_STATE_TCP_RECEIVE_DATA;
            break;
        }        
        
        case COMMUNICATION_STATE_TCP_RECEIVE_DATA:
        {
            ret = GSM_Communication_ReceivingData(communication_recv_buffer + communication_recv_size);
            if (ret > 0)
            {
                communication_recv_size += ret;
                communication_recv_buffer[communication_recv_size] = 0;
                if (GSM_ParseHTTP(communication_recv_buffer, communication_recv_size))
                {
                    communication_recv_size = GSM_TCP_ReceiveData(communication_recv_buffer);
                    if (communication_recv_size >= 0)
                    {
                        timeOutCount = 0;
                        communication_success = TRUE;
                        communication_state = COMMUNICATION_STATE_PROCESS_RECVDATA;
                        vbb_OUTPUT_Print("INFO: HTTP communication ends successfully.\r\n");
                        vbb_CONSOLE_Printf("INFO: Received %d bytes.\r\n", communication_recv_size);
                    }
                    else
                    {
                        communication_success = FALSE;
                        communication_state = COMMUNICATION_STATE_TCP_CLOSE;                
                        vbb_OUTPUT_Print("ERROR: HTTP communication ends unsuccessfully.\r\n");
                    }
                }
            }
            else if (ret == 0)
            {
                communication_state = COMMUNICATION_STATE_WAIT_FOR_SERVER;
                communication_wait_start = vbb_RTC_GetSec();
            }
            else if (ret == -1)
            {
                communication_success = FALSE;
                communication_state = COMMUNICATION_STATE_TCP_CLOSE;                
                vbb_OUTPUT_Print("ERROR: HTTP communication ends unsuccessfully.\r\n");
            }
            break;
        }
        
        case COMMUNICATION_STATE_TCP_CLOSE:
        {
            GSM_TCP_CloseClient();            
            vbb_OUTPUT_Print("INFO: Free up communication resources.\r\n");
            communication_inprocess = FALSE;
            break;
        }
        
        case COMMUNICATION_STATE_PROCESS_RECVDATA:
        {
            if (communication_success)
            {
                vbb_OUTPUT_Print("INFO: Start process response from server.\r\n");
                GSM_Communication_ProcessResponse(communication_recv_buffer, communication_recv_size);
                
                temp_int = 6000 / (vbb_RTC_GetCurrSec() - communication_lastsuccessful_time);
                communication_rate_int = temp_int / 100;
                communication_rate_fra = temp_int % 100;
                
                communication_lastsuccessful_time = vbb_RTC_GetCurrSec();                                
            }                        
            
            if (!communication_needKeepAlive)
                communication_state = COMMUNICATION_STATE_TCP_CLOSE;
            else
                communication_inprocess = FALSE;
            break;
        }
    }
    return TRUE;
}

bool GSM_Communication_LastSuccess(void)
{
    return communication_success;
}

#endif
